﻿using SqlSugar;
using Snowflake.Core;
using System.Threading.Tasks;
using System;
using Common.Framework.Core.Common;
using Common.Framework.Core.Const;
using System.Linq.Expressions;
using System.Reflection;
using Common.Framework.Core.Extension;
using System.Collections.Generic;
using System.Linq;
using SqlSugar.IOC;
using Common.Framework.Core.Cache;
using System.Configuration;

namespace Common.Framework.Core.SqlSugar
{
    /// <summary>
    /// 用于普通的数据库操作
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    public class BaseRepository<TEntity> : SimpleClient<TEntity>, IBaseRepository<TEntity> where TEntity : BaseEntity, new()
    {
        /// <summary>
        /// 雪花id
        /// </summary>
        private readonly IdWorker idWorker;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="context"></param>
        /// <param name="idWorker"></param>
        public BaseRepository(ISqlSugarClient context, IdWorker idWorker) : base(context)
        {
            this.idWorker = idWorker;
        }

        /// <summary>
        /// 表名
        /// </summary>
        protected string TableName => typeof(TEntity).Name;

        public ISqlSugarClient Connection()
        {
            string defaultConnectionString = "DefaultConnectionString";
            if (!string.IsNullOrWhiteSpace(ConnectionStringName))
            {
                defaultConnectionString = ConnectionStringName;
            }

            base.Context = DbScoped.SugarScope.GetConnection(defaultConnectionString);
            // DbScoped.SugarScope.ChangeDatabase(defaultConnectionString)            

            return base.Context;
        }

        /// <summary>
        /// 当前对象访问数据库时需要用到的数据库链接字符串
        /// </summary>
        private string ConnectionStringName
        {
            //get
            //{
            //    string connName = "";
            //    var tmpConnectionNameAttribute = typeof(TEntity).GetCustomAttributes().FirstOrDefault(s => s.TypeId.ToString() == "Common.Framework.Core.Common.ConnectionNameAttribute");
            //    if (tmpConnectionNameAttribute != null)
            //    {
            //        connName = (tmpConnectionNameAttribute as ConnectionNameAttribute).ConnectionStringName;
            //    }
            //    return connName;
            //}

            get
            {
                string cacheKey = $"reflect_conn:{TableName}";
                string connName = MemoryCache.Instance.Get<string>(cacheKey);
                if (string.IsNullOrWhiteSpace(connName))
                {
                    var tmpConnectionNameAttribute = typeof(TEntity).GetCustomAttributes().FirstOrDefault(s => s.TypeId.ToString() == "Common.Framework.Core.Common.ConnectionNameAttribute");
                    if (tmpConnectionNameAttribute != null)
                    {
                        connName = (tmpConnectionNameAttribute as ConnectionNameAttribute).ConnectionStringName;
                    }
                    else
                    {
                        //如果没有找到分库链接特性,则赋值默认值
                        connName = "DefaultConnectionString";
                    }
                    MemoryCache.Instance.SetSliding(cacheKey, connName, 86400);
                }
                return connName;
            }
        }

        public long GenerateId()
        {
            return this.idWorker.NextId();
        }

        #region 增
        public async new Task<TEntity> InsertAsync(TEntity entity)
        {
            DateTime nowTime = DateTime.Now;
            if (entity.Id == 0)
            {
                entity.Id = GenerateId();
            }
            entity.DeleteFlag = CommonConst.DeleteFlagUnDelete;
            entity.CreateTime = entity.CreateTime ?? nowTime;
            entity.UpdateTime = entity.UpdateTime ?? nowTime;
            var insertResult = await Connection().Insertable(entity).ExecuteCommandAsync() > 0;
            if (!insertResult) //插入失败时,设置Id为0,可用于业务判断
            {
                entity.Id = 0;
            }
            return entity;
        }

        public async Task<bool> InsertsAsync(List<TEntity> entities)
        {
            //如果集合为空,直接返回操作成功
            if (entities.Count <= 0)
            {
                return true;
            }
            DateTime nowTime = DateTime.Now;
            foreach (var e in entities)
            {
                if (e.Id == 0)
                {
                    e.Id = GenerateId();
                }
                e.DeleteFlag = CommonConst.DeleteFlagUnDelete;
                e.CreateTime = e.CreateTime ?? nowTime;
                e.UpdateTime = e.UpdateTime ?? nowTime;
            }
            var insertResult = await Connection().Insertable(entities).ExecuteCommandAsync();
            return insertResult > 0;
        }
        /// <summary>
        /// 分表插入
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public async  Task<TEntity> SplitTableInsertAsync(TEntity entity)
        {
            DateTime nowTime = DateTime.Now;
            if (entity.Id == 0)
            {
                entity.Id = GenerateId();
            }
            entity.DeleteFlag = CommonConst.DeleteFlagUnDelete;
            entity.CreateTime = entity.CreateTime ?? nowTime;
            entity.UpdateTime = entity.UpdateTime ?? nowTime;
            var insertResult = await Connection().Insertable(entity).SplitTable().ExecuteCommandAsync() > 0;
            if (!insertResult) //插入失败时,设置Id为0,可用于业务判断
            {
                entity.Id = 0;
            }
            return entity;
        }
        /// <summary>
        /// 分表插入
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public async  Task<bool> SplitTableInsertAsync(List<TEntity> entities)
        {

            //如果集合为空,直接返回操作成功
            if (entities.Count <= 0)
            {
                return true;
            }
            DateTime nowTime = DateTime.Now;
            foreach (var e in entities)
            {
                if (e.Id == 0)
                {
                    e.Id = GenerateId();
                }
                e.DeleteFlag = CommonConst.DeleteFlagUnDelete;
                e.CreateTime = e.CreateTime ?? nowTime;
                e.UpdateTime = e.UpdateTime ?? nowTime;
            }
            var insertResult = await Connection().Insertable(entities).SplitTable().ExecuteCommandAsync();
            return insertResult > 0;
        }
        #endregion

        #region 删
        public async Task<bool> SoftDeleteByIdAsync(long id)
        {
            var model = new TEntity { Id = id, UpdateTime = DateTime.Now, DeleteFlag = CommonConst.DeleteFlagDelete };
            return await Connection().Updateable(model).Where(s => s.Id == id).UpdateColumns(s => new { s.UpdateTime, s.DeleteFlag }).ExecuteCommandAsync() > 0;
        }

        public async Task<bool> SoftDeleteBatchByIdsAsync(List<long> ids)
        {
            List<TEntity> entities = new List<TEntity>();
            DateTime nowTime = DateTime.Now;
            foreach (long id in ids)
            {
                entities.Add(new TEntity { Id = id, UpdateTime = nowTime, DeleteFlag = CommonConst.DeleteFlagDelete });
            }
            var updateResult = await Connection().Updateable(entities)
                .Where(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete)
                .UpdateColumns(s => new { s.UpdateTime, s.DeleteFlag })
                .ExecuteCommandAsync();
            return updateResult > 0;
        }

        public async Task<bool> RealDeleteByIdAsync(long id)
        {
            return await Connection().Deleteable<TEntity>().In(id).ExecuteCommandAsync() > 0;
        }

        #endregion

        #region 改
        public async Task<bool> UpdateByIdAsync(TEntity entity, long id)
        {
            if (id <= 0)
            {
                return false;
            }
            entity.UpdateTime = DateTime.Now;

            var updateResult = await Connection().Updateable(entity)
                .IgnoreColumns(s => new { s.CreateTime, s.DeleteFlag })
                .Where(s => s.Id == id && s.DeleteFlag == CommonConst.DeleteFlagUnDelete)
                .ExecuteCommandAsync();
            return updateResult > 0;
        }

        public async Task<bool> UpdateByIdAsync(Expression<Func<TEntity, TEntity>> updateDataByColumns, long id)
        {
            if (id <= 0)
            {
                return false;
            }
            var updateResult = await Connection().Updateable<TEntity>()
                .SetColumns(updateDataByColumns)
                .SetColumnsIF(true, s => s.UpdateTime == DateTime.Now)
                .IgnoreColumns(s => new { s.CreateTime, s.DeleteFlag })
                .Where(s => s.Id == id && s.DeleteFlag == CommonConst.DeleteFlagUnDelete).ExecuteCommandAsync();
            return updateResult > 0;
        }

        public async Task<bool> UpdateByExpressionAsync(Expression<Func<TEntity, TEntity>> updateDataByColumns, Expression<Func<TEntity, bool>> whereExpression)
        {
            if (whereExpression == null)
            {
                return false;
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            var updateResult = await Connection().Updateable<TEntity>()
                .SetColumns(updateDataByColumns)
                .SetColumnsIF(true, s => s.UpdateTime == DateTime.Now)
                .IgnoreColumns(s => new { s.CreateTime, s.DeleteFlag })
                .Where(whereExpression)
                .ExecuteCommandAsync();
            return updateResult > 0;
        }

        public async Task<bool> UpdatesByIdAsync(List<TEntity> entities)
        {
            //如果集合为空,直接返回操作成功
            if (entities.Count <= 0)
            {
                return true;
            }
            var nowTime = DateTime.Now;
            foreach (var e in entities)
            {
                e.UpdateTime = nowTime;
            }
            var updateResult = await Connection().Updateable(entities)
                .IgnoreColumns(s => new { s.CreateTime, s.DeleteFlag })
                .ExecuteCommandAsync();
            return updateResult > 0;
        }

        public async Task<bool> UpdatesByExpressionAsync(List<TEntity> entities, Expression<Func<TEntity, bool>> whereExpression)
        {
            if (whereExpression == null)
            {
                return false;
            }
            var nowTime = DateTime.Now;
            foreach (var e in entities)
            {
                e.UpdateTime = nowTime;
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            var updateResult = await Connection().Updateable(entities)
                .Where(whereExpression)
                .IgnoreColumns(s => new { s.CreateTime, s.DeleteFlag })
                .ExecuteCommandAsync();
            return updateResult > 0;
        }

        #endregion

        #region 查
        public async Task<TEntity> QueryByIdAsync(long id, Expression<Func<TEntity, TEntity>> selector = null, bool noLock = false)
        {
            if (id <= 0)
            {
                return null;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            linq = linq.Where(s => s.Id == id && s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            var queryResult = await linq.FirstAsync();
            return queryResult;
        }
        public async Task<TEntity> QueryByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TEntity>> selector = null, Expression<Func<TEntity, object>> orderByColumns = null, OrderByType orderType = OrderByType.Desc, bool noLock = false)
        {
            if (whereExpression == null)
            {
                return null;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }
            if (orderByColumns != null)
            {
                linq = linq.OrderBy(orderByColumns, orderType);
            }
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);

            var queryResult = await linq.FirstAsync();
            return queryResult;
        }
        public async Task<List<TEntity>> QueryListByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression, Expression<Func<TEntity, TEntity>> selector = null, Expression<Func<TEntity, object>> orderByColumns = null, OrderByType orderType = OrderByType.Desc, bool noLock = false, bool distinct = false)
        {
            if (whereExpression == null)
            {
                return null;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }

            if (!distinct)
            {
                if (orderByColumns != null)
                {
                    linq = linq.OrderBy(orderByColumns, orderType);
                }
                else
                {
                    linq = linq.OrderBy(s => s.Id, OrderByType.Desc);
                }
            }
            else
            {
                linq = linq.Distinct();
            }

            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);


            var queryResult = await linq.ToListAsync();
            return queryResult;
        }
        public async Task<PageResult<TEntity>> QueryPageListByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression, int pageIndex, int pageSize, Expression<Func<TEntity, TEntity>> selector = null, Expression<Func<TEntity, object>> orderByColumns = null, OrderByType orderType = OrderByType.Desc, bool noLock = false)
        {
            if (whereExpression == null)
            {
                return null;
            }
            if (pageIndex == 0)
            {
                pageIndex = 1;
            }
            if (pageSize == 0)
            {
                pageSize = 10;
            }

            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }
            if (orderByColumns != null)
            {
                linq = linq.OrderBy(orderByColumns, orderType);
            }
            else
            {
                linq = linq.OrderBy(s => s.Id, OrderByType.Desc);
            }
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);

            RefAsync<int> resultCount = 0;
            var queryResult = await linq.ToPageListAsync(pageIndex, pageSize, resultCount);
            return new PageResult<TEntity>
            {
                Data = queryResult,
                Total = resultCount
            };
        }
        public async Task<int> CountByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression, bool noLock = false)
        {
            if (whereExpression == null)
            {
                return 0;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);

            var queryResult = await linq.CountAsync();
            return queryResult;
        }

    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="TableName"></param>
        /// <param name="selector"></param>
        /// <param name="noLock"></param>
        /// <returns></returns>
        public async Task<TEntity> SplitTableQueryByIdAsync(long id, string TableName, Expression<Func<TEntity, TEntity>> selector = null, bool noLock = false)
        {
            if (id <= 0)
            {
                return null;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }
            linq = linq.Where(s => s.Id == id && s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            if (!string.IsNullOrEmpty(TableName))
            {
                linq = linq.SplitTable(tas => tas.InTableNames(TableName));
            }
          
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
       
            var queryResult = await linq.FirstAsync();
            return queryResult;
        }
        /// <summary>
        /// 分表根据CreateTime字段查询
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <param name="BeginDate">CreateTime开始时间</param>
        /// <param name="EndDate">CreateTime结束时间</param>
        ///  <param name="TableNames">精确表</param>
        /// <param name="selector"></param>
        /// <param name="orderByColumns"></param>
        /// <param name="orderType"></param>
        /// <param name="noLock"></param>
        /// <param name="distinct"></param>
        /// <returns></returns>
        public async Task<List<TEntity>> SplitTableQueryListByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression,string[] TableNames, DateTime? BeginDate=null, DateTime? EndDate=null, Expression<Func<TEntity, TEntity>> selector = null, Expression<Func<TEntity, object>> orderByColumns = null, OrderByType orderType = OrderByType.Desc, bool noLock = false, bool distinct = false)
        {
            if (whereExpression == null)
            {
                return null;
            }
            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }

          
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);
            if (TableNames != null && TableNames.Length > 0)
            {
                linq = linq.SplitTable(tas => tas.InTableNames(TableNames));
            }
            else if(BeginDate!=null&& EndDate!=null)
            {
                linq = linq.SplitTable(Convert.ToDateTime( BeginDate),Convert.ToDateTime( EndDate));
            }
            if (!distinct)
            {
                if (orderByColumns != null)
                {
                    linq = linq.OrderBy(orderByColumns, orderType);
                }
                else
                {
                    linq = linq.OrderBy(s => s.Id, OrderByType.Desc);
                }
            }
            else
            {
                linq = linq.Distinct();
            }

            var queryResult = await linq.ToListAsync();
            return queryResult;
        }
        /// <summary>
        /// 分表分页查询
        /// </summary>
        /// <param name="whereExpression"></param>
        /// <param name="BeginDate">CreateTime开始时间</param>
        /// <param name="EndDate">CreateTime结束时间</param>
        /// <param name="TableNames">精确表</param>
        /// <param name="pageIndex"></param>
        /// <param name="pageSize"></param>
        /// <param name="selector"></param>
        /// <param name="orderByColumns"></param>
        /// <param name="orderType"></param>
        /// <param name="noLock"></param>
        /// <returns></returns>
        public async Task<PageResult<TEntity>> SplitTableQueryPageListByExpressionAsync(Expression<Func<TEntity, bool>> whereExpression, string[] TableNames, int pageIndex, int pageSize, DateTime? BeginDate = null, DateTime? EndDate = null, Expression<Func<TEntity, TEntity>> selector = null, Expression<Func<TEntity, object>> orderByColumns = null, OrderByType orderType = OrderByType.Desc, bool noLock = false)
        {
            if (whereExpression == null)
            {
                return null;
            }
            if (pageIndex == 0)
            {
                pageIndex = 1;
            }
            if (pageSize == 0)
            {
                pageSize = 10;
            }

            ISugarQueryable<TEntity> linq = Connection().Queryable<TEntity>();
            if (selector != null)
            {
                linq = linq.Select(selector);
            }
      
            if (noLock)
            {
                linq = linq.With(SqlWith.NoLock);
            }
            whereExpression = whereExpression.And(s => s.DeleteFlag == CommonConst.DeleteFlagUnDelete);
            linq = linq.Where(whereExpression);
            if (TableNames != null&& TableNames.Length>0)
            {
                linq = linq.SplitTable(tas=>tas.InTableNames(TableNames));
            }
            else if (BeginDate != null && EndDate != null)
            {
                linq = linq.SplitTable(Convert.ToDateTime(BeginDate), Convert.ToDateTime(EndDate));
            }
            if (orderByColumns != null)
            {
                linq = linq.OrderBy(orderByColumns, orderType);
            }
            else
            {
                linq = linq.OrderBy(s => s.Id, OrderByType.Desc);
            }
            RefAsync<int> resultCount = 0;
            var queryResult = await linq.ToPageListAsync(pageIndex, pageSize, resultCount);
            return new PageResult<TEntity>
            {
                Data = queryResult,
                Total = resultCount
            };
        }

        #endregion
    }
}